/* eslint-disable no-param-reassign */
import {
  awaitTimeout,
  debounceAndAsyncMerge,
  RuntimeError,
  RuntimeModelError,
} from '@ibiz-template/core';
import { IDEGrid, IDEGridGroupColumn, IDEGridColumn } from '@ibiz/model-core';
import {
  calcDeCodeNameById,
  CodeListItem,
  ControlVO,
  getGridColumnProvider,
  GridNotifyState,
  IGridColumnProvider,
  IGridController,
  IGridEvent,
  IGridState,
  isValueChange,
  MDControlController,
  MDCtrlLoadParams,
  Srfuf,
} from '@ibiz-template/runtime';
import { clone } from 'ramda';
import { GridFieldColumnController } from '../grid-column/grid-field-column/grid-field-column.controller';
import { GridColumnController } from './grid-column.controller';
import { GridRowState } from './grid-row.state';
import { GridService } from './grid.service';
import {
  GridFieldEditColumnController,
  GridUAColumnController,
} from '../grid-column';

/**
 * 表格控制器
 *
 * @author chitanda
 * @date 2022-08-01 18:08:13
 * @export
 * @class GridController
 * @extends {MDControlController<GridModel>}
 */
export class GridController
  extends MDControlController<IDEGrid, IGridState, IGridEvent>
  implements IGridController
{
  /**
   * 表格部件服务
   *
   * @author lxm
   * @date 2022-08-19 13:08:51
   * @type {EntityService}
   */
  declare service: GridService;

  /**
   * 是否有配置宽度自适应列
   *
   * @type {boolean}
   * @memberof GridController
   */
  hasAdaptiveColumn: boolean = false;

  /**
   * 是否有多级表头
   * @author lxm
   * @date 2023-08-07 02:26:16
   * @type {boolean}
   */
  isMultistageHeader: boolean = false;

  /**
   * 所有表格列控制器集合
   *
   * @author lxm
   * @date 2022-11-14 15:11:14
   * @type {{ [key: string]: GridColumnController }}
   */
  columns: { [key: string]: GridColumnController } = {};

  /**
   * 所有表格属性列的控制器
   *
   * @author lxm
   * @date 2022-08-24 20:08:07
   * @type {{ [key: string]: GridFieldColumnController }}
   */
  fieldColumns: { [key: string]: GridFieldColumnController } = {};

  /**
   * 所有表格操作列的控制器
   *
   * @author lxm
   * @date 2022-08-24 20:08:07
   * @type {{ [key: string]: GridUAColumnController }}
   */
  uaColumns: { [key: string]: GridUAColumnController } = {};

  /**
   * 所有表格编辑列的控制器
   *
   * @author lxm
   * @date 2022-08-24 20:08:07
   * @type {{ [key: string]: GridFieldEditColumnController }}
   */
  editColumns: { [key: string]: GridFieldEditColumnController } = {};

  /**
   * 表格列的适配器
   *
   * @author lxm
   * @date 2022-11-14 14:11:39
   * @type {{ [key: string]: IGridColumnProvider }}
   */
  providers: { [key: string]: IGridColumnProvider } = {};

  /**
   * 分组属性列控制器
   * @author lxm
   * @date 2023-08-07 09:45:30
   * @type {string}
   */
  groupFieldColumn?: GridFieldColumnController;

  /**
   * 分组代码表项集合
   * @author lxm
   * @date 2023-08-07 09:09:42
   * @type {readonly}
   */
  get groupCodeListItems(): Readonly<CodeListItem[]> | undefined {
    return this.groupFieldColumn?.codeListItems;
  }

  /**
   * 是否启用表格聚合
   * @author lxm
   * @date 2023-08-07 04:10:55
   * @readonly
   * @type {boolean}
   */
  get enableAgg(): boolean {
    return this.model.aggMode !== 'NONE';
  }

  /**
   * 聚合行标题
   * @author lxm
   * @date 2023-08-07 04:11:00
   * @type {string}
   */
  aggTitle: string = '合计';

  protected initState(): void {
    super.initState();
    this.state.rows = [];
    this.state.noSort = this.model.noSort === true;
    this.state.size = this.model.pagingSize || 20;
    this.state.singleSelect = this.model.singleSelect === true;
    this.state.columnStates = [];
    this.state.aggResult = {};
  }

  /**
   * 初始化方法
   *
   * @author lxm
   * @date 2022-08-18 22:08:17
   * @protected
   * @returns {*}  {Promise<void>}
   */
  protected async onCreated(): Promise<void> {
    await super.onCreated();

    this.service = new GridService(this.model);
    await this.service.init(this.context);
    await this.initGridColumns();

    // !数据变更通知防抖，且合并参数,如果有同一批触发不同行的值变更则该方法需要重写
    this.dataChangeNotify = debounceAndAsyncMerge(
      this.dataChangeNotify.bind(this),
      (arr1, arr2): [GridRowState, string[]] => {
        return [arr1[0], Array.from(new Set([...arr1[1], ...arr2[1]]))];
      },
      200,
    );

    // 设置默认排序
    this.setSort();

    // 初始化表格分组
    await this.initGroup();
  }

  /**
   * 初始化表格分组
   * @author lxm
   * @date 2023-08-07 09:10:17
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async initGroup(): Promise<void> {
    if (!this.model.enableGroup) {
      return;
    }
    const { groupAppDEFieldId, groupCodeListId, groupMode } = this.model;
    this.groupFieldColumn = Object.values(this.fieldColumns).find(
      item => item.model.appDEFieldId === groupAppDEFieldId,
    );
    if (!this.groupFieldColumn) {
      throw new RuntimeModelError(this.model, '没有配置分组属性的属性列');
    }
    const groupFieldName = this.groupFieldColumn.model.id;
    if (!this.groupFieldColumn.model.appCodeListId) {
      throw new RuntimeModelError(
        this.groupFieldColumn.model,
        `分组属性的属性列${groupFieldName}没有配置代码表`,
      );
    }

    // 找到分类属性列的位置，如果不在第一列调整到第一列去
    const index = this.state.columnStates.findIndex(
      item => item.key === this.groupFieldColumn!.model.codeName,
    );
    if (index !== -1 && index !== 0) {
      if (this.isMultistageHeader) {
        throw new RuntimeModelError(
          this.model,
          `请将分组属性列${groupFieldName}配置为第一列`,
        );
      }
      // 如果找到了元素，则将其从原来的位置删除
      const removeEls = this.state.columnStates.splice(index, 1);
      // 将元素插入到数组的第一位
      this.state.columnStates.unshift(...removeEls);
    }

    if (groupMode === 'CODELIST') {
      if (!groupCodeListId) {
        throw new RuntimeModelError(this.model, '代码表分组模式需要配置代码表');
      }
      if (this.groupFieldColumn.model.appCodeListId !== groupCodeListId) {
        throw new RuntimeModelError(
          this.model,
          `分组代码表与属性列${groupFieldName}的代码表不一致`,
        );
      }
    }
  }

  async afterLoad(
    args: MDCtrlLoadParams,
    items: ControlVO[],
  ): Promise<ControlVO[]> {
    await super.afterLoad(args, items);
    // 每次表格刷新时通知表格属性列,加载代码表,避免动态代码表更新不及时
    await Promise.allSettled(
      Object.values(this.fieldColumns).map(async fieldColumn => {
        await fieldColumn.loadCodeList();
      }),
    );

    // 生成表格row对象
    this.state.rows = items.map(item => {
      const row = new GridRowState(item, this);
      this.gridStateNotify(row, GridNotifyState.LOAD);
      return row;
    });

    // 响应式写法，用state里遍历出来的row才是reactive
    this.state.rows.forEach(row => {
      Object.values(row.uaColStates).forEach(uaState => {
        uaState.update(row.data.getOrigin(), this.model.appDataEntityId);
      });
    });

    this.calcGroupData(items);
    // await this.loadRemoteAgg();
    this.calcAggResult(items);

    return items;
  }

  /**
   * 计算分组数据
   * @author lxm
   * @date 2023-08-07 02:16:39
   * @protected
   * @param {IData[]} items
   */
  protected calcGroupData(items: IData[]): void {
    const { enableGroup, groupMode } = this.model;
    if (enableGroup) {
      const groupMap = new Map<string | number, IData[]>();
      const codeListGroup = groupMode === 'CODELIST';
      const groupFiledName = this.groupFieldColumn!.model.id!;
      if (codeListGroup) {
        this.groupCodeListItems!.forEach(item => {
          groupMap.set(item.value, []);
        });
      }
      items.forEach(item => {
        const groupFieldValue = item[groupFiledName];
        // 非代码表分组时，不存在时根据数据创建分组
        if (!codeListGroup && !groupMap.has(groupFieldValue)) {
          groupMap.set(groupFieldValue, []);
        }
        if (groupMap.has(groupFieldValue)) {
          groupMap.get(groupFieldValue)!.push(item);
        }
      });
      this.state.groups = [];
      groupMap.forEach((value, key) => {
        const codeListItem = this.groupCodeListItems!.find(
          item => item.value === key,
        );
        this.state.groups.push({
          caption: codeListItem?.text || `${key}`,
          children: value,
        });
      });
    }
  }

  /**
   * 加载远程聚合数据
   * @author lxm
   * @date 2023-08-07 05:35:36
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async loadRemoteAgg(): Promise<void> {
    const { aggMode, aggAppDataEntityId, aggAppDEDataSetId } = this.model;
    if (aggMode !== 'ALL') {
      return;
    }
    if (!aggAppDEDataSetId || !aggAppDataEntityId) {
      throw new RuntimeModelError(this.model, '缺少配置聚合实体或聚合数据集');
    }
    const params = await this.getFetchParams();
    const app = ibiz.hub.getApp(this.context.srfappid);
    const res = await app.deService.exec(
      aggAppDataEntityId,
      aggAppDEDataSetId,
      this.context,
      undefined,
      params,
    );

    if (res.data.length) {
      this.state.remoteAggResult = res.data[0];
    }
  }

  /**
   * 计算当前页的聚合数据
   * @author lxm
   * @date 2023-08-07 04:22:09
   * @protected
   * @param {IData[]} items
   */
  protected calcAggResult(items: IData[]): void {
    Object.values(this.fieldColumns).forEach(column => {
      const result = column.calcFieldAgg(items);
      if (result) {
        this.state.aggResult[column.model.id!] = result;
      }
    });
  }

  /**
   * 后台删除结束后界面删除逻辑
   *
   * @author lxm
   * @date 2022-09-06 19:09:10
   * @param {IData} data
   */
  afterRemove(data: IData): void {
    super.afterRemove(data);
    // 删除this.state.rows里的表格行数据
    const index = this.state.rows.findIndex(
      item => item.data.srfkey === data.srfkey,
    );
    this.state.rows.splice(index, 1);

    // 删除分组里的那条数据
    this.state.groups.forEach(item => {
      if (item.children.length) {
        const findIndex = item.children.findIndex(
          item2 => item2.srfkey === data.srfkey,
        );
        if (findIndex !== -1) {
          item.children.splice(findIndex, 1);
        }
      }
    });
  }

  /**
   * 新建行
   *
   * @author lxm
   * @date 2022-09-06 21:09:05
   */
  async newRow(): Promise<void> {
    const res = await this.service.getDraft(this.context, this.params);
    // 加载完后续处理
    this.state.items.unshift(res.data);
    const newRow = new GridRowState(res.data, this);
    this.state.rows.unshift(newRow);
    this.gridStateNotify(newRow, GridNotifyState.DRAFT);
  }

  /**
   * 保存
   *
   * @author lxm
   * @date 2022-09-06 19:09:21
   * @param {ControlVO} data
   * @returns {*}  {Promise<void>}
   */
  async save(data: ControlVO): Promise<void> {
    if (this.state.isSimple) {
      return;
    }
    const rowState = this.state.rows.find(
      item => item.data.srfkey === data.srfkey,
    );
    if (!rowState) {
      throw new RuntimeError('行数据不存在');
    }
    if (!rowState.modified) {
      ibiz.log.debug('值没有发生改变');
      return;
    }

    // 如果数据正在处理中，则延迟保存
    if (rowState.processing) {
      await awaitTimeout(500, this.save.bind(this), [data]);
      return;
    }

    const isValid = await this.validate(rowState);
    if (!isValid) {
      throw new RuntimeError('行数据校验不通过，保存取消');
    }
    await this.startLoading();
    let res;
    const isCreate = data.srfuf === Srfuf.CREATE;
    const deName = calcDeCodeNameById(this.model.appDataEntityId!);
    const tempContext = this.context.clone();
    tempContext[deName] = data.srfkey;
    try {
      res = isCreate
        ? await this.service.create(tempContext, data)
        : await this.service.update(tempContext, data);
    } finally {
      await this.endLoading();
    }
    // 后续处理
    const index = this.state.items.findIndex(
      item => item.srfkey === data.srfkey,
    );
    this.state.items.splice(index, 1, res.data);
    rowState.data = res.data;
    rowState.modified = false;

    ibiz.message.success(`${res.data.srfmajortext || ''}保存成功`);
    this.gridStateNotify(rowState, GridNotifyState.SAVE);
    await this.evt.emit('onSaveSuccess', undefined);
  }

  /**
   * 初始化表格属性列，操作列，编辑项控制器
   *
   * @author lxm
   * @date 2022-08-24 21:08:48
   * @protected
   */
  protected async initColumnsController(column: IDEGridColumn): Promise<void> {
    // 初始化适配器
    const provider = await getGridColumnProvider(column);
    if (!provider) {
      return;
    }
    this.providers[column.codeName!] = provider;

    // 初始化表格列控制器
    const controller = await provider.createController(column, this);
    this.columns[column.codeName!] = controller as GridColumnController;

    // 分类存放控制器
    if (column.columnType === 'DEFGRIDCOLUMN') {
      this.fieldColumns[column.codeName!] =
        controller as GridFieldColumnController;
      if (column.enableRowEdit) {
        this.editColumns[column.codeName!] =
          controller as GridFieldEditColumnController;
      }
      this.state.columnStates.push({
        key: column.codeName!,
        caption: column.caption!,
        hidden: !!column.hideDefault,
      });
    } else if (column.columnType === 'UAGRIDCOLUMN') {
      this.uaColumns[column.codeName!] = controller as GridUAColumnController;
    } else if (column.columnType === 'GROUPGRIDCOLUMN') {
      // 有分组列模型说明就是有多级表头
      this.isMultistageHeader = true;
      await Promise.all(
        (column as IDEGridGroupColumn).degridColumns?.map(async childColumn => {
          await this.initColumnsController(childColumn);
        }) || [],
      );
    }
  }

  /**
   * 初始化表格属性列，操作列，编辑项控制器
   *
   * @author lxm
   * @date 2022-08-24 21:08:48
   * @protected
   */
  protected async initGridColumns(): Promise<void> {
    if (this.model.degridColumns) {
      await Promise.all(
        this.model.degridColumns.map(async column =>
          this.initColumnsController(column),
        ),
      );
    }
  }

  /**
   * 设置行属性的值
   *
   * @author lxm
   * @date 2022-08-24 10:08:40
   * @param {GridRowState} row 行状态控制器
   * @param {unknown} value 要设置的值
   * @param {string} name 要设置的表单数据的属性名称
   */
  async setRowValue(
    row: GridRowState,
    name: string,
    value: unknown,
  ): Promise<void> {
    if (
      Object.prototype.hasOwnProperty.call(row.data, name) &&
      !isValueChange(row.data[name], value)
    ) {
      // `表格行数据里没有属性${name}或者${name}的值未发生改变`
      return;
    }
    // 改变值
    row.data[name] = value;
    row.modified = true;
    row.processing = true;

    try {
      await this.dataChangeNotify(row, [name]);
      await this.evt.emit('onGridDataChange', {
        data: this.state.rows.map(_row => {
          return _row.data;
        }),
      });
    } finally {
      row.processing = false;
    }
  }

  /**
   * 通知所有表格编辑项成员表格编辑项数据变更
   *
   * @author lxm
   * @date 2022-09-20 22:09:49
   * @param {GridRowState} row 行数据
   * @param {string[]} names 更新的属性
   */
  async dataChangeNotify(row: GridRowState, names: string[]): Promise<void> {
    // 通知所有编辑项去处理编辑项相关逻辑
    const values = await Promise.allSettled(
      Object.values(this.editColumns).map(async column => {
        return column.dataChangeNotify(row, names);
      }),
    );
    if (values.find(item => item.status === 'rejected')) {
      ibiz.log.error('dataChangeNotify报错', values);
      throw new RuntimeError('数据变更后有错误');
    }
  }

  /**
   * 表格状态变更通知
   *
   * @author lxm
   * @date 2022-09-20 18:09:07
   */
  gridStateNotify(row: GridRowState, state: GridNotifyState): void {
    // 通知表格编辑列
    Object.values(this.editColumns).forEach(column => {
      column.gridStateNotify(row, state);
    });
  }

  /**
   * 校验一行数据的所有编辑项
   *
   * @author lxm
   * @date 2022-09-06 21:09:05
   * @param {GridRowState} row 要校验的行数据控制器
   * @returns {*}
   */
  async validate(row: GridRowState): Promise<boolean> {
    const values = await Promise.all(
      Object.values(this.editColumns).map(column => column.validate(row)),
    );

    // 找不到value为false即全部是true
    return values.findIndex(value => !value) === -1;
  }

  /**
   * 切换行编辑，行编辑切换关闭前会校验并保存，如果失败则依然显示行编辑状态
   *
   * @author lxm
   * @date 2022-09-08 11:09:55
   * @param {GridRowState} row 行数据控制器
   */
  async toggleRowEdit(row?: GridRowState): Promise<void> {
    if (!row) {
      this.state.rows.forEach(async _row => {
        await this.toggleRowEdit(_row);
      });
      return;
    }
    if (row.showRowEdit === true) {
      await this.save(row.data);
    }
    row.showRowEdit = !row.showRowEdit;
  }

  /**
   * 设置排序
   *
   * @author lxm
   * @date 2022-09-28 13:09:44
   * @param {string} key 排序字段
   * @param {string} order 排序顺序
   */
  setSort(key?: string, order?: 'asc' | 'desc'): void {
    if (key && order) {
      super.setSort(key, order);
    } else {
      // 设置默认排序或者置空
      const { minorSortAppDEFieldId, minorSortDir } = this.model;
      if (minorSortAppDEFieldId && minorSortDir) {
        this.state.sortQuery = `${minorSortAppDEFieldId.toLowerCase()},${minorSortDir.toLowerCase()}`;
      } else {
        this.state.sortQuery = '';
      }
    }
  }

  /**
   * 表格编辑项更新
   *
   * @author lxm
   * @date 2022-09-15 21:09:13
   * @param {string} methodName 更新实体方法
   * @param {string[]} updateItems 更新项名称集合
   */
  async updateGridEditItem(row: GridRowState, updateId: string): Promise<void> {
    const findUpdate = this.model.degridEditItemUpdates?.find(
      item => item.id === updateId,
    );

    if (!findUpdate) {
      throw new RuntimeError(`没找到${updateId}编辑列更新`);
    }

    const { appDEMethodId, degeiupdateDetails } = findUpdate;
    const updateItems = degeiupdateDetails!.map(item => item.id!);

    const params = { ...this.params, ...row.data.getOrigin() };
    const res = await this.service.updateGridEditItem(
      appDEMethodId!,
      this.context,
      params,
    );
    const result = res.data;
    if (result && updateItems?.length) {
      await Promise.all(
        updateItems.map(itemName => {
          return this.setRowValue(row, itemName, result[itemName]);
        }),
      );
    }
  }

  /**
   * 加载数据(只加载数据 不做其他操作)
   *
   * @author zk
   * @date 2023-07-20 04:07:49
   * @param {MDCtrlLoadParams} args
   * @return {*}
   * @memberof GridController
   */
  async loadData(args: MDCtrlLoadParams): Promise<ControlVO[]> {
    // *查询参数处理
    const { context } = this.handlerAbilityParams(args);
    const params = await this.getFetchParams(args?.params);
    let res;
    // *发起请求
    await this.startLoading();
    try {
      res = await this.service.fetch(context, params);
    } finally {
      await this.endLoading();
    }
    return res.data;
  }

  /**
   * 数据导出
   *
   * @param {MouseEvent} event 原生js点击事件
   * @returns {*}  {Promise<void>}
   * @memberof MDControlController
   */
  async exportData(_args: { event: MouseEvent; params: IData }): Promise<void> {
    // 前台导出
    const items: { [key: string]: GridFieldColumnController } =
      this.fieldColumns;
    // 数据格式化
    const formatExcelData = (data: IData[]): IData[] => {
      const codeListMap: Map<string, readonly CodeListItem[]> = new Map();
      Object.keys(items).forEach((key: string) => {
        if (items[key].codeList) {
          codeListMap.set(key, items[key].codeListItems!);
        }
      });
      const cloneData = clone(
        data.map(item => {
          return item.getOrigin();
        }),
      );
      cloneData.forEach(item => {
        const fields = Object.keys(item);
        fields.forEach((key: string) => {
          if (codeListMap.get(key)) {
            item[key] =
              codeListMap.get(key)!.find(_item => {
                return _item.value === item[key];
              })?.text || item[key];
          }
        });
      });
      return cloneData;
    };
    const getExportData = async (): Promise<IData[]> => {
      const type = _args.params?.type;
      let data: IData[] = [];
      if (type === 'activatedPage') {
        data = this.state.rows.map(row => row.data);
      }
      if (type === 'maxRowCount' || type === 'customPage') {
        const { size } = this.state;
        const { startPage, endPage } = _args.params;
        const params =
          type === 'customPage'
            ? {
                page: 0,
                offset: (startPage - 1) * size,
                size: (endPage - startPage + 1) * size,
              }
            : { size: 1000, page: 0 };
        data = await this.loadData({ params });
      }
      if (type === 'selectedRows') {
        data = this.getData();
      }
      if (data.length === 0) {
        throw new RuntimeError('无导出数据');
      }
      return formatExcelData(data);
    };

    //  一维数组转成二维数组
    const formatArrayData = (
      filterVal: string[],
      tableData: IData[],
    ): IData[] => {
      return tableData.map(v => {
        return filterVal.map(j => {
          return v[j];
        });
      });
    };
    // 头部id数组
    const filterVal = Object.keys(items).map(
      (key: string) => items[key].model.id!,
    );
    // 表头数组
    const header = Object.keys(items).map(
      (key: string) => items[key].model.caption,
    );
    // 二维数组
    const data = formatArrayData(filterVal, await getExportData());
    const exportExcel = await import(
      '../../../util/xlsx-util/xlsx-util.ts' as string
    );
    if (!exportExcel) {
      throw new RuntimeError('导出模块加载错误');
    }
    exportExcel.exportJsonToExcel({
      header, // 表头内容 数组格式
      data, // 具体数据 这是个二维数组
      filename: this.model.logicName, // 文件名称
      autoWidth: true, // 单元格是否自适应
    });
  }
}
